package org.lexicon.swpogo;

import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;

import org.codehaus.groovy.runtime.InvokerHelper;

import groovy.lang.DelegatingMetaClass;
import groovy.lang.MetaClass;
import groovy.lang.MissingPropertyException;
import groovy.util.Expando;
import com.hp.hpl.jena.rdf.model.*;

/**
 * A delegating meta class that adds property change support to a Groovy object.
 */
public class SemWebPogoPropertyChangeMetaClass extends DelegatingMetaClass {
	private PropertyChangeSupport support = new PropertyChangeSupport(this);

	public SemWebPogoPropertyChangeMetaClass(MetaClass metaClass) {
		super(metaClass);
	}

	// --- Property change support methods ---

	public void addPropertyChangeListener(PropertyChangeListener l) {
		support.addPropertyChangeListener(l);
	}

	public void addPropertyChangeListener(String propertyName,
			PropertyChangeListener l) {
		support.addPropertyChangeListener(propertyName, l);
	}

	public void removePropertyChangeListener(PropertyChangeListener l) {
		support.removePropertyChangeListener(l);
	}

	public void removePropertyChangeListener(String propertyName,
			PropertyChangeListener l) {
		support.removePropertyChangeListener(propertyName, l);
	}

	// --- MetaObject overrides ---

	@Override
	public Object invokeMethod(Object object, String methodName, Object[] arguments) {
		if (methodName.equals("addPropertyChangeListener")) {
			if (arguments.length == 1) {
				addPropertyChangeListener((PropertyChangeListener) arguments[0]);
			} 
			else {
				addPropertyChangeListener((String) arguments[0], (PropertyChangeListener) arguments[1]);
			}
			return null;
		} 
		else if (methodName.equals("removePropertyChangeListener")) {
			if (arguments.length == 1) {
				removePropertyChangeListener((PropertyChangeListener) arguments[0]);
			} 
			else {
				removePropertyChangeListener((String) arguments[0],
						(PropertyChangeListener) arguments[1]);
			}
			return null;
		} 
		else if (object.rdfSetterMethods.containsKey(methodName)) {
			this.setProperty(object, object.rdfSetterMethods.get(methodName),arguments[0]);
		}
		else if (methodName.equals("setUri")) {
			
		}
		return super.invokeMethod(object, methodName, arguments);
	}

	@SuppressWarnings("unchecked")
	@Override
	public void setProperty(Object object, String property, Object newValue) {
		Object oldValue = InvokerHelper.getProperty(object, property);
		if ((oldValue == newValue) || (oldValue != null && oldValue.equals(newValue))){
			return;
		}
		else {
			if (object.rdfReadWriteMethods.containsKey(property)) {
				//create old value model
				Model model = ModelFactory.createDefaultModel();
				model.setNsPrefixes(object.getNamespaces());
				
				if (oldValue != null) {
					//remove old value model
					Model returnModel = object.invokeMethod(object.rdfReadWriteMethods.get(property).get(1), [model, oldValue]);
					if (returnModel != null) model.updateModel(property, returnModel, false);
				}
				if (newValue != null) {
					//add new model
					Model newModel = object.invokeMethod(object.rdfReadWriteMethods.get(property).get(1), [model, newValue]);
					if (newModel != null) object.updateModel(property, newModel, true);
				}
			}
			else {
				object.updateModel(object.class.getDeclaredField(property), oldValue, newValue);
			}
		}
		try {
			super.setProperty(object, property, newValue);
			support.firePropertyChange(property, oldValue, newValue);
		} 

		catch (MissingPropertyException e) {
			// Special case handling for Expando, will go away with new MOP.
			if (object instanceof Expando) {
				((Expando)object).getProperties().put(property, newValue);
				support.firePropertyChange(property, oldValue, newValue);
			} 
			else {
				throw e;
			}
		}
	}
}

